import React, { memo, useCallback, useEffect, useImperativeHandle, useRef, useState, useMemo } from 'react';
import cn from 'classnames';
import { message, Popconfirm, Spin } from 'antd';
import debounce from 'lodash/debounce';
import useUploadOss from '@/hooks/useUploadOss';
import usePreventUnload from '@/hooks/usePreventUnload';
import AudioRecorder from '@/utils/recorder';
import { findParentByClassName } from '@/utils/utils';
import styles from './index.less';

let timer;
let Float = ({ children, show }) => {
    const [translateData, setTranslateData] = useState({
        translateX: 0,
        translateY: 0
    });
    const [staticData] = useState({
        moveAble: false,
        originX: 0,
        originY: 0,
        lastX: 0,
        lastY: 0,
        diffX: 0,
        diffY: 0
    });

    usePreventUnload();

    const getTarget = event => event.target || event.srcElement;
    const touchMove = e => {
        e.preventDefault();
        if (!staticData.moveAble) {
            return;
        }
        let curTransferX = staticData.lastX + e.clientX - staticData.originX;
        let curTransferY = staticData.lastY + e.clientY - staticData.originY;
        // 左偏移最大值
        if (curTransferX <= staticData.rangeX[0]) {
            [curTransferX] = staticData.rangeX;
        }
        // 右偏移最大值
        if (curTransferX >= staticData.rangeX[1]) {
            [, curTransferX] = staticData.rangeX;
        }
        // 上偏移最大值
        if (curTransferY <= staticData.rangeY[0]) {
            [curTransferY] = staticData.rangeY;
        }
        // 下偏移最大值
        if (curTransferY >= staticData.rangeY[1]) {
            [, curTransferY] = staticData;
        }
        setTranslateData({
            translateX: curTransferX,
            translateY: curTransferY
        });
    };

    const touchStart = e => {
        e.preventDefault();
        let target = getTarget(e);
        // eslint-disable-next-line no-const-assign
        while (target.offsetParent) {
            target = target.offsetParent;
        }
        staticData.moveAble = true;
        staticData.originX = e.clientX;
        staticData.originY = e.clientY;
        staticData.lastX = translateData.translateX;
        staticData.lastY = translateData.translateY;
        /**
         * 边界判断逻辑
         * x轴translate可设置的范围区间为[-目标左偏移量, (可视窗口宽度 - 目标左偏移量 - 目标宽度)]
         * y轴translate可设置的范围区间为[-目标上偏移量, (可视窗口高度 - 目标上偏移量 - 目标高度)]
         */
        staticData.rangeX = [-target.offsetLeft, document.body.clientWidth - target.offsetLeft - target.offsetWidth];
        staticData.rangeY = [-target.offsetTop, document.body.clientHeight - target.offsetTop - target.offsetHeight];
        document.addEventListener('mousemove', touchMove);
        getTarget(e).addEventListener('mouseup', () => {
            e.preventDefault();
            document.removeEventListener('mousemove', touchMove);
            staticData.moveAble = false;
        });
    };
    return (
        <div
            className={styles.float}
            style={{
                display: show ? 'flex' : 'none',
                transform: `translate(${translateData.translateX}px, ${translateData.translateY}px)`
            }}
        >
            {children}
            {/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
            <i
                className={cn('iconfont iconlistfunction_drag_icon_default', styles.startAudioEndIcon)}
                onMouseDown={touchStart}
            />
        </div>
    );
};
Float = memo(Float);

const renderButton = ({
    start,
    finished,
    time = '00:00:00',
    handleAudioStart,
    handleAudioStop,
    onBeforeStart,
    recordStatusMsg
}) => {
    if (recordStatusMsg) {
        return (
            <>
                <span className={styles.isRecording}>
                    <i className={cn('iconfont iconrecordIcon', styles.startAudioIcon)} /> {recordStatusMsg}
                </span>
            </>
        );
    }

    if (start) {
        return (
            <>
                <Popconfirm
                    title={
                        <div>
                            <div>确定要停止录制?</div>
                            <div className={styles.recordHint}>录音一旦停止不可再次开启</div>
                        </div>
                    }
                    onConfirm={handleAudioStop}
                    okText="停止"
                    getPopupContainer={() => document.body}
                    // getPopupContainer={trigger => (move ? document.body : trigger.parentNode.parentNode)}
                    autoAdjustOverflow
                >
                    <div className={styles.audioButtonContent}>
                        <i className={cn('iconfont iconstop_record_Icon', styles.startAudioingIcon)} />
                        <span style={{ fontSize: 16 }}>{time}</span>
                    </div>
                </Popconfirm>
            </>
        );
    }
    if (!start && !finished) {
        return (
            <Popconfirm
                title="确定要开始录制?"
                onConfirm={() => {
                    if (onBeforeStart) {
                        onBeforeStart().then(() => {
                            handleAudioStart();
                        });
                        return;
                    }
                    handleAudioStart();
                }}
                getPopupContainer={() => document.body}
                // getPopupContainer={trigger => (move ? document.body : trigger.parentNode.parentNode)}
                autoAdjustOverflow={false}
            >
                <div className={styles.audioButtonContent}>
                    <i className={cn('iconfont iconrecordIcon', styles.startAudioIcon)} />
                    <span style={{ fontSize: 16 }}>开始录音</span>
                </div>
            </Popconfirm>
        );
    }
    if (!start && finished) {
        return (
            <div className={styles.audioButtonContent}>
                <i className={cn('iconfont iconrecordIcon', styles.startAudioIcon)} />
                <span style={{ fontSize: 16 }}>{time}</span>
            </div>
        );
    }
    return null;
};

let RecordButtonContent = props => {
    const {
        start = false,
        finished = false,
        move = false,
        loading = false,
        recordStatusMsg,
        isGuide = false,
        queryLoading,
        guideMsg,
        guideKey = 'interview_guide'
    } = props;
    const guideRef = useRef();
    const userCode = useMemo(() => {
        const user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : {};
        return user?.userCode;
    }, []);
    const handleShowGuide = useCallback(() => {
        localStorage.setItem(`${guideKey}_${userCode}`, 'true');
    }, []);
    useEffect(() => {
        const guided = localStorage.getItem(`${guideKey}_${userCode}`);
        if (!queryLoading && isGuide && !guided && guideRef.current) {
            guideRef.current.setVisible(true);
        }
    }, [queryLoading]);
    return (
        <Spin spinning={loading}>
            <div className={styles.startAudioButtonWrap}>
                {!move && (
                    <Popconfirm
                        ref={guideRef}
                        autoAdjustOverflow={false}
                        overlayClassName={styles.guideWrap}
                        title={guideMsg}
                        cancelText={null}
                        okType="link"
                        icon={null}
                        okText="我知道了"
                        getPopupContainer={() => document.body}
                        // getPopupContainer={trigger => trigger.parentNode}
                        onVisibleChange={visible => {
                            if (visible) handleShowGuide();
                        }}
                    >
                        <div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }} />
                    </Popconfirm>
                )}
                <div
                    className={cn(styles.startAudioButton, {
                        [styles.startAudioButtonStatic]: !move,
                        [styles.startAudioButtonHover]: !start && !finished
                    })}
                    style={{ pointerEvents: recordStatusMsg ? 'none' : 'auto' }}
                >
                    {renderButton({ ...props })}
                </div>
            </div>
        </Spin>
    );
};

RecordButtonContent = memo(RecordButtonContent);

/**
 * 开始录音组件
 *
 * @param { 滚动容器下层级的Ref  } scrollWrapRef
 * @param { 是否可录音方法 } getRecordState
 * @param { 录音开始执行的回调 } onChangeRecordState
 * @param { 函数组件传入的Ref } cRef
 */
const RecordButton = props => {
    const {
        scrollWrapRef,
        getRecordState = () => false,
        onChangeRecordState = () => {},
        cRef,
        hintList = {},
        ...rest
    } = props;
    const [uploadFile] = useUploadOss();
    const [isStart, setIsStart] = useState(false);
    const [isFinished, setIsFinished] = useState(false);
    const [isFloat, setIsFloat] = useState(false);
    const [currentTime, setCurrentTime] = useState('00:00:00');
    const [audioLoading, setAudioLoading] = useState(false);
    const buttonRef = useRef();
    const scrollRef = useRef();
    const [recordTimeInfo] = useState({});
    const recRef = useRef();
    // tab-scroll-context

    const getPoint = obj => {
        let t = obj.offsetTop;
        // eslint-disable-next-line no-cond-assign
        while ((obj = obj.offsetParent)) {
            t += obj.offsetTop;
        }
        return t;
    };
    const scrollHandle = useCallback(() => {
        if (buttonRef.current && scrollRef.current) {
            const instance = getPoint(buttonRef.current) - getPoint(scrollRef.current);
            // if (scrollRef.current.scrollTop > (instance + buttonRef.current.offsetHeight)) {
            //     console.log('移出了可视区')
            // } else {
            //     console.log('还在可视区')
            // }
            setIsFloat(scrollRef.current.scrollTop > instance + buttonRef.current.offsetHeight);
        }
    }, []);
    const scrollHandleRef = useRef(debounce(() => scrollHandle(), 200)).current;
    const handleAudioUpload = async () => {
        try {
            setAudioLoading(true);
            const blob = recRef.current.getBlob();
            const file = new window.File([blob], `${+new Date()}.mp3`, { type: blob.type });
            const result = await uploadFile(file);
            setAudioLoading(false);
            return result;
        } catch (e) {
            setAudioLoading(false);
            return '';
        }
    };
    const handleAudioStop = () =>
        new Promise((resolve, reject) => {
            clearInterval(timer);
            recRef.current.recStop(resolve, reject);
        })
            .then(() => {
                recordTimeInfo.endTime = new Date().getTime();
                setIsStart(false);
                setIsFinished(true);
                onChangeRecordState(2);
                return true;
            })
            .catch(e => {
                console.log(e, '失败的理由');
                return false;
            });
    const handleAudioStart = async () => {
        try {
            if (!isStart && !isFinished) {
                setAudioLoading(true);
                const state = await getRecordState();
                if (!state) {
                    recRef.current.recStart(() => {
                        onChangeRecordState(1);
                        setIsStart(true);
                        recordTimeInfo.startTime = new Date().getTime();
                        clearInterval(timer);
                        timer = setInterval(() => {
                            console.log(recRef.current.getRecDuration());
                            setCurrentTime(recRef.current.getRecDuration());
                        }, 1000);
                    });
                } else {
                    message.error(hintList[state] || '当前已有录音进行中！');
                }
                setAudioLoading(false);
            }
        } catch (e) {
            setAudioLoading(false);
            console.log(e);
        }
    };
    useImperativeHandle(cRef, () => ({
        getRecordInfo: async () => {
            const params = {};
            if (isStart && !isFinished) {
                const state = await handleAudioStop();
                if (state) {
                    params.recordUrl = await handleAudioUpload();
                }
            } else if (isFinished) {
                params.recordUrl = await handleAudioUpload();
            }
            if (params.recordUrl) {
                params.recordTimeInfo = recordTimeInfo;
            }
            return params;
        }
    }));
    // eslint-disable-next-line consistent-return
    useEffect(() => {
        if (scrollWrapRef && scrollWrapRef.current) {
            const obj = findParentByClassName(scrollWrapRef.current, 'tab-scroll-context');
            scrollRef.current = obj;
            obj.addEventListener('scroll', scrollHandleRef, true);
            return () => {
                obj.removeEventListener('scroll', scrollHandleRef, true);
            };
        }
    }, []);

    useEffect(() => {
        recRef.current = new AudioRecorder();
        recRef.current.recInitialize();
        return () => {
            recRef.current.destroy();
            if (timer) clearInterval(timer);
        };
    }, []);

    // 兜底无法上传的情况，按钮上下载当前录音
    const handleContextMenu = e => {
        e.preventDefault();
        console.log('===开始下载当前录音===');
        const blob = recRef.current.getBlob();
        if (!blob) {
            console.log('===不存在录音===');
            return;
        }
        const link = document.createElement('a');
        const body = document.querySelector('body');

        link.href = window.URL.createObjectURL(blob);
        link.download = '录音.mp3';

        // fix Firefox
        link.style.display = 'none';
        body.appendChild(link);

        link.click();
        body.removeChild(link);

        window.URL.revokeObjectURL(link.href);
    };

    const recordProps = {
        start: isStart,
        finished: isFinished,
        time: currentTime,
        handleAudioStop,
        handleAudioStart,
        loading: audioLoading,
        ...rest
    };

    return (
        <div ref={buttonRef} onContextMenu={handleContextMenu}>
            <RecordButtonContent {...recordProps} />
            <Float show={isFloat}>
                <RecordButtonContent move {...recordProps} />
            </Float>
        </div>
    );
};

export default memo(RecordButton);
